﻿using AsmShaderConverter.HlslShader;
using System;
using System.Collections.Generic;

namespace AsmShaderConverter.AsmShader
{
    public class AsmShaderInputOutput : IConvertToHlsl, IComparable
    {
        public enum DataDirection
        {
            In,
            Out
        }

        public string RegisterName { get { return this.register.OriginalRegisterName; } }

        private string originalLineOfCode;
        public AsmSemantics semantic;
        private Register register;
        private DataDirection dataDirection;

        public Register GetDestinationRegister()
        {
            return this.register;
        }

        public AsmShaderInputOutput(string lineOfCode)
        {
            if (lineOfCode.Contains("//"))
                lineOfCode = lineOfCode.Substring(0, lineOfCode.IndexOf("/"));

            originalLineOfCode = lineOfCode;
            var values = lineOfCode.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

            switch (values.Length)
            {
                case 2:

                    switch (values[1].Substring(0, 1))
                    {
                        case "v": this.dataDirection = DataDirection.In; break;
                        case "o": this.dataDirection = DataDirection.Out; break;
                        default: throw new Exception("Can´t determine the datadirection. " + lineOfCode);
                    }

                    this.register = new Register(values[1], Swizzle.StandardXYZW);

                    this.semantic = AsmShader.AsmShaderHelper.ConvertTo(values[0]);

                    break;

                default:
                    throw new Exception("Wrong number of values" + this.originalLineOfCode);
            }
        }

        public string ToHLSL(AsmShaderFile asmShader)
        {
            int highestSemantic = 0;
            for (int i = 0; i < this.register.swizzle.Length; i++)
            {
                switch (register.swizzle[i])
                {
                    case 'x': highestSemantic = Math.Max(1, highestSemantic); break;
                    case 'y': highestSemantic = Math.Max(2, highestSemantic); break;
                    case 'z': highestSemantic = Math.Max(3, highestSemantic); break;
                    case 'w': highestSemantic = Math.Max(4, highestSemantic); break;
                }
            }

            if (highestSemantic == 0) highestSemantic = 4;

            string typename = (highestSemantic != 1) ? string.Format("float{0}", ((highestSemantic == 0) ? 4 : highestSemantic).ToString()) : "float";
            //string typename = (this.register.swizzle.Length != 1) ? string.Format("float{0}", ((this.register.swizzle.Length == 0) ? 4 : this.register.swizzle.Length).ToString()) : "float";
            var semantic = AsmShader.AsmShaderHelper.ConvertAsmSemantivToHlsl(this.semantic, asmShader.ShaderType, asmShader.shaderVersion, this.dataDirection).ToString();
            if (semantic == HlslSemantics.SV_Position.ToString())
                typename = "float4";

            return string.Format("{0} {1} : {2}", typename, this.register.GetRegisterName(asmShader), semantic);
        }

        public string ToHLSLWitchComment(AsmShaderFile asmShader)
        {
            return this.ToHLSL(asmShader) + " /* " + this.originalLineOfCode + " */ ";
        }

        public int CompareTo(object obj)
        {
            if (obj.GetType() == typeof(AsmShaderInputOutput))
            {
                AsmShaderInputOutput asi = obj as AsmShaderInputOutput;
                int tt = Convert.ToInt32(asi.semantic);
                int tt2 = Convert.ToInt32(this.semantic);
                int tst = tt2.CompareTo(tt);

                return tst;
            }

            return 0;
        }

        /// <summary>
        /// Adds missing Shader Imputs to destination from reference lsit.
        /// </summary>
        /// <param name="pixelShaderInput"></param>
        /// <param name="vertexShaderOutput"></param>
        internal static void AddMissingRegisterToPixelShaderInput(List<AsmShaderInputOutput> pixelShaderInput, List<AsmShaderInputOutput> vertexShaderOutput)
        {
            foreach (AsmShaderInputOutput item in vertexShaderOutput)
            {
                bool isMissing = true;
                if (item.semantic == AsmSemantics.dcl_position) // do not add dcl_position to output
                {
                    continue;
                }

                for (int i = 0; i < pixelShaderInput.Count; i++)
                {
                    if (pixelShaderInput[i].semantic == item.semantic)
                    {
                        if (pixelShaderInput[i].register.swizzle.Length != item.register.swizzle.Length)
                        {
                            item.register.swizzle = new char[pixelShaderInput[i].register.swizzle.Length];
                            item.register.swizzle.CopyTo(pixelShaderInput[i].register.swizzle, 0);
                        }
                        isMissing = false;
                        break;
                    }
                }

                if (isMissing)
                {
                    pixelShaderInput.Add(new AsmShaderInputOutput(item.originalLineOfCode));
                }
            }
            pixelShaderInput.Sort();
        }

        public override string ToString()
        {
            return string.Format("{0} {1} // {2}", this.RegisterName, this.semantic, this.originalLineOfCode);
        }
    }
}